/*--------------------------------------------------------------
 * File: device1.c 编码类型：C语言  UTF-8
 * Author WRS (1352509846@qq.com)
 * 功能: 模拟的设备1功能
 * Version 0.1
 * Date 2024-04-29 
 * 
 * @copyright Copyright (c) 2024
 * 
----------------------------------------------------------------*/
#include "device1.h"
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "data.h"
// #include "user_Modbus.h"
#include "mdns.h"
#include "esp_netif.h"
#include "esp_mac.h"
#include "mdns.h"
#include "esp_netif.h"
#include "mbcontroller.h"       // for mbcontroller defines and api
#include "data.h"

// Define Modbus register counts
#define MB_REG_HOLD_CNT     (8)
#define MB_REG_INPUT_CNT    (8)
#define MB_REG_COIL_CNT     (8)

// Defines below are used to define register start address for each type of Modbus registers
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) >> 1))
#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) >> 1))
#define MB_REG_DISCRETE_INPUT_START         (0x0000)
#define MB_REG_COILS_START                  (0x0000)
#define MB_REG_INPUT_START_AREA0            (INPUT_OFFSET(input_data0)) // register offset input area 0
#define MB_REG_INPUT_START_AREA1            (INPUT_OFFSET(input_data4)) // register offset input area 1
#define MB_REG_HOLDING_START_AREA0          (HOLD_OFFSET(holding_data0))
#define MB_REG_HOLDING_START_AREA1          (HOLD_OFFSET(holding_data4))

#define MB_PAR_INFO_GET_TOUT                (20) // Timeout for get parameter info
#define MB_CHAN_DATA_MAX_VAL                (10)
#define MB_CHAN_DATA_OFFSET                 (1)

#define MB_READ_MASK (MB_EVENT_INPUT_REG_RD | MB_EVENT_HOLDING_REG_RD | MB_EVENT_DISCRETE_RD | MB_EVENT_COILS_RD)
#define MB_WRITE_MASK (MB_EVENT_HOLDING_REG_WR | MB_EVENT_COILS_WR)
#define MB_READ_WRITE_MASK (MB_READ_MASK | MB_WRITE_MASK)

static const char *TAG = "Device1 ";

TaskHandle_t Device1_task_Handle = NULL;

coil_reg_params_t       coil_reg_params = {0};        // 线圈寄存器
input_reg_params_t      input_reg_params = {0};       // 输入寄存器缓冲区
holding_reg_params_t    holding_reg_params = {0};     // 保持寄存器缓冲区

/*--------------------------------------------------------------
 * 功能: 模拟的设备1初始化
 * 
----------------------------------------------------------------*/
esp_err_t Device1_init(void)
{
    esp_err_t err = 0;
    mb_register_area_descriptor_t reg_area; // Modbus register area descriptor structure

    user_Modbus_init();

    // 添加modbus寄存器的初始化---------------------------------
    reg_area.type = MB_PARAM_COIL;
    reg_area.start_offset = MB_REG_COILS_START;
    reg_area.address = (void*)&coil_reg_params;
    reg_area.size = sizeof(coil_reg_params);
    err = mbc_slave_set_descriptor(reg_area);
    if (err != ESP_OK) 
    {
        ESP_LOGE(TAG, "Failed to set coil_reg_params descriptor.");
        return err;
    }

    reg_area.type = MB_PARAM_HOLDING; // Set type of register area
    reg_area.start_offset = MB_REG_HOLDING_START_AREA0; // Offset of register area in Modbus protocol
    reg_area.address = (void*)&holding_reg_params.holding_data0; // Set pointer to storage instance
    reg_area.size = sizeof(holding_reg_params); // Set the size of register storage instance
    err = mbc_slave_set_descriptor(reg_area);
    if (err != ESP_OK) 
    {
        ESP_LOGE(TAG, "Failed to set holding_reg_params descriptor.");
        return err;
    }

    reg_area.type = MB_PARAM_INPUT;
    reg_area.start_offset = MB_REG_INPUT_START_AREA0;
    reg_area.address = (void*)&input_reg_params.input_data0;
    reg_area.size = sizeof(input_reg_params);
    err = mbc_slave_set_descriptor(reg_area);
    if (err != ESP_OK) 
    {
        ESP_LOGE(TAG, "Failed to set input_reg_params descriptor.");
        return err;
    }
    // 添加modbus寄存器的初始化--------------------------------- 

    user_Modbus_slave_start();
    return ESP_OK;
}

static void Device1_operation_func(void *arg)
{
    mb_param_info_t reg_info; // keeps the Modbus registers access information

    // ESP_LOGI(TAG, "Modbus slave stack initialized.");
    // ESP_LOGI(TAG, "Start modbus test...");
    // The cycle below will be terminated when parameter holding_data0
    // incremented each access cycle reaches the CHAN_DATA_MAX_VAL value.
    // Check for read/write events of Modbus master for certain events
    mb_event_group_t event = mbc_slave_check_event(MB_READ_WRITE_MASK);
    const char *rw_str = (event & MB_READ_MASK) ? "READ" : "WRITE";
    // Filter events and process them accordingly
    if (event & (MB_EVENT_HOLDING_REG_WR | MB_EVENT_HOLDING_REG_RD))
    {
        // Get parameter information from parameter queue
        ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
        ESP_LOGI(TAG, "HOLDING %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
                 rw_str,
                 (unsigned)reg_info.time_stamp,
                 (unsigned)reg_info.mb_offset,
                 (unsigned)reg_info.type,
                 (int)reg_info.address,
                 (unsigned)reg_info.size);
        if (reg_info.address == (uint8_t *)&holding_reg_params.holding_data0)
        {
            holding_reg_params.holding_data0 = Random_data_in_range(1, 20);
        }
    }
    else if (event & MB_EVENT_INPUT_REG_RD)
    {
        ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
        ESP_LOGI(TAG, "INPUT READ (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
                 (unsigned)reg_info.time_stamp,
                 (unsigned)reg_info.mb_offset,
                 (unsigned)reg_info.type,
                 (int)reg_info.address,
                 (unsigned)reg_info.size);
        if (reg_info.address == (uint8_t *)&input_reg_params.input_data1)
        {
            input_reg_params.input_data1 = Random_data_in_range(20, 40);
        }
    }
    else if (event & MB_EVENT_DISCRETE_RD)
    {
        ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
        ESP_LOGI(TAG, "DISCRETE READ (%u us): ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
                 (unsigned)reg_info.time_stamp,
                 (unsigned)reg_info.mb_offset,
                 (unsigned)reg_info.type,
                 (int)reg_info.address,
                 (unsigned)reg_info.size);
    }
    else if (event & (MB_EVENT_COILS_RD | MB_EVENT_COILS_WR))
    {
        ESP_ERROR_CHECK(mbc_slave_get_param_info(&reg_info, MB_PAR_INFO_GET_TOUT));
        ESP_LOGI(TAG, "COILS %s (%u us), ADDR:%u, TYPE:%u, INST_ADDR:0x%.4x, SIZE:%u",
                 rw_str,
                 (unsigned)reg_info.time_stamp,
                 (unsigned)reg_info.mb_offset,
                 (unsigned)reg_info.type,
                 (int)reg_info.address,
                 (unsigned)reg_info.size);
    }
}

void Device1_task(void *parm)
{
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(10));
        Device1_operation_func(NULL);
    }    
}

/*--------------------------------------------------------------
 * 功能: 模拟设备1启动
 * 
----------------------------------------------------------------*/
void Device1_Start(void)
{
    holding_reg_params.holding_data0 = 1;
    holding_reg_params.holding_data1 = 2;
    holding_reg_params.holding_data2 = 3;
    holding_reg_params.holding_data3 = 4;
    holding_reg_params.test_regs[0] = 5;
    holding_reg_params.test_regs[1] = 6;
    holding_reg_params.test_regs[49] = 7;
    holding_reg_params.holding_data4 = 8;

    Device1_init();
    xTaskCreate(&Device1_task, "Device1_task", 8192, NULL, 6, &Device1_task_Handle);
}

/*--------------------------------------------------------------
 * 功能: 模拟设备1停止
 * 
----------------------------------------------------------------*/
void Device1_stop(void)
{
    vTaskSuspend(Device1_task_Handle);
    vTaskDelay(pdMS_TO_TICKS(100));
    vTaskDelete(Device1_task_Handle);
    Device1_task_Handle = NULL;
    user_Modbus_slave_destroy();    
}